// это объект, который хранит объекты интерпретатора
// и графического контекста; только этот объект
// обрабатывает события от пользователя и браузера:
// таймер, изменения размеров окна, пуск, остановка
// пауза, загрузка...

function executor(draw_element, w, h, on_exception) {
  this.code = '';
  this.gc = new draw(draw_element, this);
  this.on_exception = on_exception;
  this.timer_id = null;
  // вызывать полный resize_handler незачем, так как
  // программа (this.code) пуста
  this.gc.vp_resize(w, h);
}

executor.prototype.load = function(code) { // public
  this.code = code;
  this.restart();
};

executor.prototype.restart = function() {
  var prs, tree;
  try {
    prs = new parse(this.code);
    // null -- первый токен выражения. тут его просто нет
    // {} -- пустой конфиг -- специальный случай для блоков
    tree = new cmd(null, prs, {});
  } catch (err) {
    this.on_exception('PARSER ERROR: ' + err.message);
    return;
  }
  //L(tree);
  var ctx = new context();
  ctx.gc = this.gc;
  this.executor = new command_executor(tree, ctx);
  // TODO: останавливаем текущий процесс
  // чистим область рисования
  this.gc.clear(100, 100, 100);
  // запускаем
  clearTimeout(this.timer_id);
  this.run();
};

executor.prototype.run = function() {
  for (var i = 0; i < 20; ++i) {
    var r;
    try {
      r = this.executor.step();
    } catch (err) {
      var s = [];
      var x;
      for (var j = 0; j < err.stack.length; ++j) {
        x = err.stack[j];
        s.push(x.repr);
      }
      //L('ERROR: ' + err.message + ' at ' + s.join('<-'));
      this.on_exception(err.message + ' at ' + s.join('<-'));
      return;
    }
    if (r) {
      this.timer_id = null;
      return;
    }
  }
  this.timer_id = setTimeout($.proxy(function() {this.run();}, this), 0);
};

executor.prototype.resize_handler = function(w, h) { // public
  // console.log([w, h]);
  this.gc.vp_resize(w, h);
  // TODO: restart только если видно?
  // или сделать полупрозрачное поле ввода кода?
  this.restart();
};
